package tasknow.commandlogic;

import java.io.File;
import java.util.Arrays;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Vector;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;

import tasknow.parser.Parser;
import tasknow.storage.DamagedXmlFileException;
import tasknow.storage.InvalidFileNameException;
import tasknow.storage.Storage;
import tasknow.ui.DisplayAndCommandTextComposite;
import tasknow.utils.Constants;
import tasknow.utils.Task;
import tasknow.utils.Comparators;

public class CommandLogic {

	// Global Variables
	public static Vector<Task> taskVector = new Vector<Task>();
	public static Vector<Task> todayTasks = new Vector<Task>();
	public static Vector<Task> nextWeekTasks = new Vector<Task>();
	public static Vector<Task> searchVector = new Vector<Task>();
	public static Vector<Task> archivedTaskVector = new Vector<Task>();

	private static Vector<Vector<Task>> listOfUndoActions = new Vector<Vector<Task>>();
	private static Vector<Vector<Task>> listOfRedoActions = new Vector<Vector<Task>>();

	private static Logger logger = Logger.getLogger("");

	private static Storage activeStorage;
	private static Storage archiveStorage;

	//@author A0098740W

	// Initiators

	public static void initiateStorage() {
		try {
			archiveStorage = new Storage(Constants.DEFAULT_ARCHIVE_FILE_NAME);
			File f = new File(Constants.DEFAULT_ARCHIVE_FILE_NAME);

			if (f.exists()) {
				archivedTaskVector = archiveStorage.readFromFile();
			} else {
				archiveStorage.saveToFile(archivedTaskVector);
			}
		} catch (DamagedXmlFileException e) {
			archivedTaskVector.clear();
			DisplayAndCommandTextComposite.showToUser(String
					.format(Constants.MSG_DAMAGED_XML_FILE,
							Constants.DEFAULT_ARCHIVE_FILE_NAME));
		} catch (InvalidFileNameException e) {
			DisplayAndCommandTextComposite
			.showToUser(Constants.MSG_INVALID_FILE_NAME);
		}

		try {
			activeStorage = new Storage(Constants.DEFAULT_FILE_NAME);
			File f = new File(Constants.DEFAULT_FILE_NAME);

			if (f.exists()) {
				taskVector = activeStorage.readFromFile();
			} else {
				activeStorage.saveToFile(taskVector);
			}
		} catch (InvalidFileNameException e) {
			DisplayAndCommandTextComposite
			.showToUser(Constants.MSG_INVALID_FILE_NAME);
		} catch (DamagedXmlFileException e) {
			taskVector.clear();
			DisplayAndCommandTextComposite.showToUser(String
					.format(Constants.MSG_DAMAGED_XML_FILE,
							Constants.DEFAULT_FILE_NAME));
		}
	}

	// End of initiator

	public static void saveFile() {
		activeStorage.saveToFile(taskVector);
	}

	// @author A0096638M

	public static String executeCommand(String[] args, Constants.COMMANDS command) {
		switch (command) {
			case Q:
				return addTask(args);
	
			case ADD:
				return addTask(args);
	
			case DELETE:
				return deleteTask(args);
	
			case DEL:
				return deleteTask(args);
	
			case D:
				return deleteTask(args);
	
			case REMOVE:
				return deleteTask(args);
	
			case CLEAR:
				return clear(args);
	
			case NEW:
				return clear(args);
	
			case ARCHIVED:
				return displayArchive();
	
			case EXIT:
				System.exit(0);
	
			case QUIT:
				System.exit(0);
	
			case SEARCH:
				return searchTasks(args);
	
			case FIND:
				return searchTasks(args);
	
			case SORT:
				return sortTasks(args);
	
			case UNDO:
				return undoActions(args);
	
			case REDO:
				return redoActions(args);
	
			case MODIFY:
				return modifyTask(args);
	
			case CHANGE:
				return modifyTask(args);
	
			case EDIT:
				return modifyTask(args);
	
			case MOD:
				return modifyTask(args);
				
			case MARK:
				return markTask(args);
	
			case DONE:
				return markTask(args);
	
			case ARCHIVE:
				return archiveTasks(args);
	
			case SAVE:
				return saveTaskVectorToFile(args[Constants.INDEX_INPUT]);
	
			case LOAD:
				return loadTaskVectorFromFile(args);
	
			case FLOAT:
				return getFloat();
	
			case INVALID:
				return String.format(Constants.ERROR_INVALID_COMMAND,
						args[Constants.INDEX_COMMAND]);
	
			default:
				break;
		}

		return Constants.DUMMY_STRING;
	}

	// @author A0096638M

	// Add

	private static String addTask(String[] args) {
		try {
			if (!isAcceptableArgumentsLength(args[Constants.INDEX_INPUT])) {
				return Constants.ERROR_ADD_ARGUMENT_TOO_LONG;
			} else if (taskVector.size() == Constants.FORMAT_TASKS_LIMIT) {
				return Constants.ERROR_ADD_TOO_MANY_TASKS;
			} else if (args[Constants.INDEX_INPUT].isEmpty()) {
				return Constants.ERROR_ADD_ARGUMENT_MISSING;
			}

			updateListOfUndoActions();
			
			Task currentTask = extractInfoAndCreateTask(args);
			
			if (currentTask.hasStartDate() && currentTask.hasEndDate()) {
				if (isBefore(currentTask.getEndDate(), currentTask.getStartDate())) {
					return Constants.ERROR_ADD_TASK_DATE;
				}
			}
			
			taskVector.add(currentTask);
			
			clearListOfRedoActions();
			sortTasks(Constants.DEFAULT_SORT);
			
			return String.format(Constants.MSG_ADD_LINE,
					currentTask.getDescription());
		} catch (ArrayIndexOutOfBoundsException e) {
			return Constants.ERROR_ADD_ARGUMENT_MISSING;
		}
	}

	private static Task extractInfoAndCreateTask(String[] args) {
		Vector<String> taskArgs = new Vector<String>(
				Arrays.asList(args[Constants.INDEX_INPUT]
						.split(Constants.BLANK_SPACE)));
		Vector<String> reversedDateAndTimeArgs = extractReversedDateAndTimeForAdd(taskArgs);
		Vector<String> dateAndTimeArgs = reverseVector(reversedDateAndTimeArgs);
		Calendar[] calendarArray = Parser.parseToCalendar(dateAndTimeArgs);
		String description = formSentence(taskArgs);

		return createTask(description,
				calendarArray[Constants.INDEX_START_CALENDAR],
				calendarArray[Constants.INDEX_END_CALENDAR]);
	}	

	private static Vector<String> extractReversedDateAndTimeForAdd(
			Vector<String> taskArgs) {
		Vector<String> vector = new Vector<String>();

		for (int i = taskArgs.size() - Constants.ADJUST_INDEX_BY_1; Parser.isValidCalendarFormat(taskArgs
				.get(i)) || Parser.isPreposition(taskArgs.get(i)); i--) {
			if (Parser.isValidCalendarFormat(taskArgs.get(i))
					|| Parser.isPreposition(taskArgs.get(i))) {
				if (!Parser.isPreposition(taskArgs.get(i))) {
					vector.add(taskArgs.get(i));
				}
				
				taskArgs.removeElementAt(i);;
			}
		}

		return vector;
	}

	private static Vector<String> reverseVector(Vector<String> vector) {
		Queue<String> queue = new LinkedList<String>();
		
		while (!vector.isEmpty()) {
			queue.add(vector.lastElement());
			vector.removeElementAt(vector.size() - Constants.ADJUST_INDEX_BY_1);;
		}

		while (!queue.isEmpty()) {
			vector.add(queue.peek());
			queue.remove();
		}

		return vector;
	}

	private static String formSentence(Vector<String> taskArgs) {
		String sentence = Constants.EMPTY_STRING;

		for (int i = 0; i < taskArgs.size(); i++) {
			sentence += taskArgs.get(i);
			sentence += Constants.BLANK_SPACE;
		}

		sentence = sentence.trim();
		sentence = sentence.substring(Constants.ZEROTH, Constants.FIRST)
				.toUpperCase()
				+ sentence.substring(Constants.FIRST, sentence.length());

		return sentence;
	}

	private static Task createTask(String description, Calendar start,
			Calendar end) {
		Task newTask = new Task(description, start, end);

		return newTask;
	}

	// End of Add

	// @author A0096638M
	// Delete

	private static String deleteTask(String[] args) {
		updateListOfUndoActions();

		Task deletedTask = null;

		if (!hasInput(args[Constants.INDEX_INPUT])) {
			return Constants.ERROR_DELETE_INVALID_INTEGER_INPUT;
		}

		try {
			int index = Integer.parseInt(args[Constants.INDEX_INPUT])
					- Constants.ADJUST_INDEX_BY_1;
			
			if (isNegative(index)) {
				return String.format(Constants.ERROR_DELETE_INVALID_ARGUMENT,
						Integer.parseInt(args[Constants.INDEX_INPUT]));
			}

			deletedTask = taskVector.get(index);
			taskVector.remove(index);
		} catch (ArrayIndexOutOfBoundsException e) {
			return Constants.ERROR_DELETE_INVALID_TASK;
		} catch (NumberFormatException nfe) {
			return String.format(Constants.ERROR_DELETE_INVALID_ARGUMENT,
					args[Constants.INDEX_INPUT]);
		} catch (NullPointerException npe) {
			return String.format(Constants.ERROR_INVALID_INPUT,
					args[Constants.INDEX_INPUT]);
		}

		clearListOfRedoActions();
		sortTasks(Constants.DEFAULT_SORT);
		
		return String
				.format(Constants.MSG_DELETE, deletedTask.getDescription());
	}

	// End of Delete

	// @author A0096638M
	// Clear

	private static String clear(String[] args) {
		updateListOfUndoActions();
		taskVector.clear();
		clearListOfRedoActions();

		return Constants.MSG_CLEAR_FILE;
	}

	// End of Clear

	// @author A0096638M
	// Search

	private static String searchTasks(String[] args) {
		String searchWord = args[Constants.INDEX_INPUT];
		searchVector.clear();

		if (!hasInput(args[Constants.INDEX_INPUT])) {
			return Constants.PROMPT_NO_SEARCH_WORD_INPUT;
		}

		try {
			if (Parser.isDateFormat(args[Constants.INDEX_INPUT])) {
				searchForDate(args[Constants.INDEX_INPUT]);
			} else {
				searchForWords(searchWord);
			}

		} catch (ArrayIndexOutOfBoundsException e) {
			DisplayAndCommandTextComposite
			.showToUser(Constants.ERROR_VECTOR_OUT_OF_BOUND);
		}

		notifyNumberOfFoundTasks(searchVector);

		return Constants.COMMAND_SEARCH;
	}

	private static void searchForWords(String searchWord) {
		String[] searchArray = searchWord.split(Constants.BLANK_SPACE);

		Vector<Task> tempSearchVector = new Vector<Task>();

		for (int i = 0; i < taskVector.size(); i++) {
			tempSearchVector.add(taskVector.get(i));
		}

		for (int i = 0; i < searchArray.length; i++) {
			for (int j = 0; j < tempSearchVector.size();) {
				if (hasWord(searchArray[i], j, tempSearchVector)) {
					addToSearchVector(searchArray[i], j, searchVector,
							tempSearchVector);
				} else {
					j++;
				}
			}
		}
	}

	private static void addToSearchVector(String searchWord, int index,
			Vector<Task> searchVector, Vector<Task> tempSearchVector) {
		searchVector.add(tempSearchVector.get(index));
		tempSearchVector.remove(index);
	}

	private static void searchForDate(String searchDate) {
		for (int i = 0; i < taskVector.size(); i++) {
			addIfContainsDate(searchDate, i);
		}
	}

	private static void addIfContainsDate(String searchDate, int index) {
		int[] date = Parser.parseDate(searchDate);
		searchDate = Integer.toString(date[Constants.DAY])
				+ String.format(Constants.FORMAT_DATE_COMPONENT,
						date[Constants.MONTH])
						+ Integer.toString(date[Constants.YEAR]);

		if (hasDate(searchDate, index)) {
			searchVector.add(taskVector.get(index));
		}
	}

	// End of Search

	// @author A0096638M
	// Sort

	private static String sortTasks(String[] args) {
		if (taskVector.isEmpty()) {
			return Constants.MSG_SORT_EMPTY_FILE;
		}

		String feedback = Constants.EMPTY_STRING;

		if (args[Constants.INDEX_INPUT].isEmpty()) {
			Collections.sort(taskVector,
					new Comparators.DescriptionComparator());
			feedback = Constants.MSG_SORTED_ALPHABETICALLY;
		} else if (args[Constants.INDEX_INPUT]
				.equalsIgnoreCase(Constants.CHARACTER_S)) {
			Collections.sort(taskVector, new Comparators.ComparatorStartDate());
			feedback = Constants.MSG_SORTED_BY_START_DATE;
		} else if (args[Constants.INDEX_INPUT]
				.equalsIgnoreCase(Constants.CHARACTER_E)) {
			Collections.sort(taskVector, new Comparators.ComparatorEndDate());
			feedback = Constants.MSG_SORTED_BY_DEADLINE;
		}

		return feedback;
	}

	// End of Sort

	// @author A0098740W
	// Undo

	private static String undoActions(String[] args) {
		if (listOfUndoActions.isEmpty()) {
			return Constants.MSG_INVALID_UNDO;
		} else if (hasNoArguments(args[Constants.INDEX_INPUT])) {
			updateUndoState();
		} else if (!Parser.isInteger(args[Constants.INDEX_INPUT])) {
			return Constants.ERROR_INVALID_NUMBER_ARGUMENT;
		} else {
			int numberOfTimesToUndo = determineMaxNumberOfUndo(args);

			for (int i = 0; i < numberOfTimesToUndo; i++) {
				updateUndoState();
			}
		}

		return Constants.MSG_UNDO_LAST_TASK;
	}

	// End of Undo

	// @author A0098740W
	// Redo

	private static String redoActions(String[] args) {
		if (listOfRedoActions.isEmpty()) {
			return Constants.MSG_INVALID_REDO;
		} else if (hasNoArguments(args[Constants.INDEX_INPUT])) {
			updateRedoState();
		} else if (!Parser.isInteger(args[Constants.INDEX_INPUT])) {
			return Constants.ERROR_INVALID_NUMBER_ARGUMENT;
		} else {
			int numberOfTimesToRedo = determineMaxNumberOfRedo(args);

			for (int i = 0; i < numberOfTimesToRedo; i++) {
				updateRedoState();
			}
		}

		return Constants.MSG_REDO_LAST_TASK;
	}

	// End of Redo

	// @author A0096638M
	// Modify

	private static String modifyTask(String[] args) {
		if (!hasInput(args[Constants.INDEX_INPUT])) {
			return Constants.ERROR_MODIFY_INVALID_INTEGER_INPUT;
		}

		try {
			String[] toModifyWithArgs = (args[Constants.INDEX_INPUT]
					.split(Constants.BLANK_SPACE));

			int toModify = Integer
					.parseInt(toModifyWithArgs[Constants.ZERO_ARGUMENT]);

			if (toModify > taskVector.size()) {
				return Constants.ERROR_MODIFY_INVALID_TASK;
			} else if (isNegative(toModify)) {
				return String.format(
						Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED,
						args[Constants.INDEX_INPUT]);
			}
		} catch (NumberFormatException nfe) {
			logger.log(Level.WARNING, String.format(
					Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED,
					args[Constants.INDEX_INPUT]));
			return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED,
					args[Constants.INDEX_INPUT]);
		}

		updateListOfUndoActions();

		String[] modArgs = args[Constants.INDEX_INPUT]
				.split(Constants.BLANK_SPACE);

		String modification = Constants.EMPTY_STRING;

		if (isModify(modArgs)) {
			if (modArgs.length == Constants.MODIFY_DATE_AND_TIME_ARGUMENTS) {
				modification = executeModify(modArgs);
			} else if (modArgs.length >= Constants.MODIFY_DATE_OR_TIME_ARGUMENTS) {
				modification = executeModify(modArgs);
			} else {
				return String.format(
						Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED,
						args[Constants.INDEX_INPUT]);
			}
		} else if (Parser.isInteger(args[Constants.INDEX_INPUT])) {
			return String.format(Constants.ERROR_MODIFY_TYPE_REQUEST);
		} else {
			return String.format(Constants.ERROR_INVALID_INPUT,
					args[Constants.INDEX_INPUT]);
		}

		clearListOfRedoActions();
		sortTasks(Constants.DEFAULT_SORT);
		
		return modification;
	}

	private static String executeModify(String[] args) {
		Constants.MODIFY_CHOICE modify_choice = getModifyType(args);
		String modification = formModificationSentence(args);
		Task taskTomodify = getModifyTask(args);

		switch (modify_choice) {
			case D:
				return modifyChoiceNumber(1, modification, taskTomodify);
	
			case S:
				return modifyChoiceNumber(2, modification, taskTomodify);
	
			case E:
				return modifyChoiceNumber(3, modification, taskTomodify);
	
			case INVALID:
				return String.format(Constants.ERROR_INVALID_INPUT,
						args[Constants.INDEX_INPUT]);
		}

		return modification;
	}

	private static String formModificationSentence(String[] args) {
		String sentence = Constants.EMPTY_STRING;

		for (int i = Constants.INDEX_MODIFY_TASK_INFORMATION_START; i < args.length; i++) {
			sentence += args[i];
			sentence += Constants.BLANK_SPACE;
		}

		sentence = sentence.trim();
		sentence = sentence.substring(Constants.ZEROTH, Constants.FIRST)
				.toUpperCase()
				+ sentence.substring(Constants.FIRST, sentence.length());

		return sentence;
	}

	private static Constants.MODIFY_CHOICE getModifyType(String[] args) {
		String modifyTypeString = getModifyTypeString(args);

		if (modifyTypeString == null) {
			throw new Error(Constants.ERROR_NULL_COMMAND_INPUT);
		}

		try {
			Constants.MODIFY_CHOICE modify = Constants.MODIFY_CHOICE
					.valueOf(modifyTypeString.trim().toUpperCase());

			return modify;
		} catch (IllegalArgumentException ex) {
			return Constants.MODIFY_CHOICE.INVALID;
		}
	}

	private static String getModifyTypeString(String[] args) {
		return args[Constants.INDEX_MODIFY_ARGUMENT];
	}

	private static Task getTask(String[] args) {
		int index = Integer.parseInt(args[Constants.INDEX_INPUT])
				- Constants.ADJUST_INDEX_BY_1;
		Task task = taskVector.get(index);

		return task;
	}

	private static Task getModifyTask(String[] args) {
		int index = Integer.parseInt(args[Constants.INDEX_MODIFY_TASK_NUMBER])
				- Constants.ADJUST_INDEX_BY_1;
		Task taskToModify = taskVector.get(index);

		return taskToModify;
	}

	private static String modifyChoiceNumber(int choice, String modification,
			Task taskToModify) {
		switch (choice) {
			case Constants.FIRST:
				taskToModify.modifyDescription(modification);
	
				return String.format(Constants.MSG_DESCRIPTION_CHANGED,
						taskToModify.getDescription());
	
			case Constants.SECOND:
				try {
					if (modification.equalsIgnoreCase(Constants.STRING_NIL_DATE)) {
						taskToModify.removeStartDate();
	
						return String.format(
								Constants.MSG_MODIFY_REMOVED_START_DATE,
								taskToModify.getDescription());
					}
	
					switch (modification.length()) {
						case Constants.LENGTH_DATE_TIME_FORMAT:
							if (!Parser.isDateTimeFormat(modification)) {
								return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED, modification);
							}

							if (taskToModify.hasEndDate()) {
								Calendar newStartCal = Parser.parseToFullCalendarFormat(modification);
								if (isBefore(taskToModify.getEndDate(), newStartCal)) {
									return Constants.ERROR_INVALID_START_MODIFICATION;
								}
							}
								String[] dateTimeArgs = modification
										.split(Constants.BLANK_SPACE);
								int[] dateInt = Parser
										.parseDate(dateTimeArgs[Constants.INDEX_DATE_ARGS]);
								int[] timeInt = Parser
										.parseTime(dateTimeArgs[Constants.INDEX_TIME_ARGS]);
								taskToModify.modifyStartDateTime(dateInt, timeInt);
			
								return String.format(Constants.MSG_START_DATE_TIME_CHANGED,
										taskToModify.getDescription(),
										taskToModify.getStartDateString(),
										taskToModify.getStartTimeString());
		
						case Constants.LENGTH_DATE_FORMAT:
							if (!Parser.isDateFormat(modification)) {
								return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED, modification);
							}
							
							if (taskToModify.hasEndDate()) {
								Calendar newStartCal = Parser.parseToFullCalendarFormat(modification);
								if (isBefore(taskToModify.getEndDate(), newStartCal)) {
									return Constants.ERROR_INVALID_START_MODIFICATION;
								}
							}
							
							int[] dateOnlyInt = Parser.parseDate(modification);
							taskToModify.modifyStartDate(dateOnlyInt);
		
							return String.format(Constants.MSG_START_DATE_CHANGED,
									taskToModify.getDescription(),
									taskToModify.getStartDateString());
		
						case Constants.LENGTH_TIME_FORMAT:
							if (!Parser.isTimeFormat(modification)) {
								return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED, modification);
							}
							
							if (!taskToModify.hasStartDate()) {
								return Constants.ERROR_INVALID_START_TIME_MODIFICATION;
							}
							
							if (taskToModify.hasEndDate()) {
								String startDate = taskToModify.getStartDateStringNoSlashModify() + Constants.BLANK_SPACE + modification;
								Calendar newStartCal = Parser.parseToFullCalendarFormat(startDate);
								if (isBefore(taskToModify.getEndDate(), newStartCal)) {
									return Constants.ERROR_INVALID_START_MODIFICATION;
								}
							}
							
							int[] timeOnlyInt = Parser.parseTime(modification);
							taskToModify.modifyStartTime(timeOnlyInt);
		
							return String.format(Constants.MSG_START_TIME_CHANGED,
									taskToModify.getDescription(),
									taskToModify.getStartTimeString());
					}
				} catch (NumberFormatException e) {
					return String.format(Constants.ERROR_INVALID_ARGUMENT_DETECTED,
							modification);
				}
	
			case Constants.THIRD:
				try {
					if (modification.equalsIgnoreCase(Constants.STRING_NIL_DATE)) {
						taskToModify.removeEndDate();
	
						return String.format(Constants.MSG_MODIFY_REMOVED_END_DATE,
								taskToModify.getDescription());
					}
	
					switch (modification.length()) {
						case Constants.LENGTH_DATE_TIME_FORMAT:
							if (!Parser.isDateTimeFormat(modification)) {
								return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED, modification);
							}
							
							if (taskToModify.hasStartDate()) {
								Calendar newEndCal = Parser.parseToFullCalendarFormat(modification);
								if (isBefore(newEndCal, taskToModify.getStartDate())) {
									return Constants.ERROR_INVALID_END_MODIFICATION;
								}
							}
							
							String[] dateTimeArgs = modification
									.split(Constants.BLANK_SPACE);
							int[] dateInt = Parser
									.parseDate(dateTimeArgs[Constants.INDEX_DATE_ARGS]);
							int[] timeInt = Parser
									.parseTime(dateTimeArgs[Constants.INDEX_TIME_ARGS]);
							taskToModify.modifyEndDateTime(dateInt, timeInt);
		
							return String.format(Constants.MSG_END_DATE_TIME_CHANGED,
									taskToModify.getDescription(),
									taskToModify.getEndDateString(),
									taskToModify.getEndTimeString());
		
						case Constants.LENGTH_DATE_FORMAT:
							if (!Parser.isDateFormat(modification)) {
								return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED, modification);
							}
							
							if (taskToModify.hasStartDate()) {
								Calendar newEndCal = Parser.parseToFullCalendarFormat(modification);
								if (isBefore(newEndCal, taskToModify.getStartDate())) {
									return Constants.ERROR_INVALID_END_MODIFICATION;
								}
							}
							
							int[] dateOnlyInt = Parser.parseDate(modification);
							taskToModify.modifyEndDate(dateOnlyInt);
		
							return String.format(Constants.MSG_END_DATE_CHANGED,
									taskToModify.getDescription(),
									taskToModify.getEndDateString());
		
						case Constants.LENGTH_TIME_FORMAT:
							if (!Parser.isTimeFormat(modification)) {
								return String.format(Constants.ERROR_MODIFY_INVALID_ARGUMENT_DETECTED, modification);
							}
							
							if (!taskToModify.hasEndDate()) {
								return Constants.ERROR_INVALID_END_TIME_MODIFICATION;
							}
							
							if (taskToModify.hasStartDate()) {
								String endDate = taskToModify.getEndDateStringNoSlashModify() + Constants.BLANK_SPACE + modification;
								Calendar newEndCal = Parser.parseToFullCalendarFormat(endDate);
								if (isBefore(newEndCal, taskToModify.getStartDate())) {
									return Constants.ERROR_INVALID_END_MODIFICATION;
								}
							}
							
							int[] timeOnlyInt = Parser.parseTime(modification);
							taskToModify.modifyEndTime(timeOnlyInt);
		
							return String.format(Constants.MSG_END_TIME_CHANGED,
									taskToModify.getDescription(),
									taskToModify.getEndTimeString());
					}
				} catch (NumberFormatException e) {
					return Constants.ERROR_INVALID_ARGUMENT_DETECTED;
				}
		}

		return Constants.DUMMY_STRING;
	}

	// End of Modify

	// @author A0096638M
	// Mark

	private static String markTask(String[] args) {
		if (!hasInput(args[Constants.INDEX_INPUT])) {
			return Constants.ERROR_MARK_INVALID_INTEGER_INPUT;
		}

		try {
			int toMark = Integer.parseInt(args[Constants.INDEX_INPUT]);

			if (toMark > taskVector.size()) {
				return Constants.ERROR_MARK_INVALID_TASK;
			} else if (isNegative(toMark)) {
				return String.format(
						Constants.ERROR_MARK_INVALID_ARGUMENT_DETECTED,
						args[Constants.INDEX_INPUT]);
			}
		} catch (NumberFormatException nfe) {
			logger.log(Level.WARNING, String.format(
					Constants.ERROR_MARK_INVALID_ARGUMENT_DETECTED,
					args[Constants.INDEX_INPUT]));
			
			return String.format(
					Constants.ERROR_MARK_INVALID_ARGUMENT_DETECTED,
					args[Constants.INDEX_INPUT]);
		} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
			return Constants.ERROR_MARK_INVALID_TASK;
		}

		updateListOfUndoActions();
		
		Task taskToMark = getTask(args);
		taskToMark.mark();
		
		clearListOfRedoActions();
		sortTasks(Constants.DEFAULT_SORT);
		
		if (taskToMark.isCompleted()) {
			return String.format(Constants.MSG_MARKED_AS_COMPLETE,
					taskToMark.getDescription());
		} else {
			return String.format(Constants.MSG_MARKED_AS_INCOMPLETE,
					taskToMark.getDescription());
		}
	}

	// End of Mark

	// @author A0096638M
	// Archive

	private static String archiveTasks(String[] args) {
		int numberOfArchivedFiles = countAndArchiveFiles();

		switch (numberOfArchivedFiles) {
			case Constants.ZEROTH:
				return Constants.FORMAT_ARCHIVE_FILES_NONE;
	
			case Constants.FIRST:
				return Constants.FORMAT_ARCHIVE_FILES_ONE;
	
			default:
				return String.format(Constants.FORMAT_ARCHIVE_FILES_MULTIPLE,
						numberOfArchivedFiles);
		}
	}

	private static int countAndArchiveFiles() {
		try {
			archivedTaskVector = archiveStorage.readFromFile();
		} catch (DamagedXmlFileException e) {
			DisplayAndCommandTextComposite.showToUser(String
					.format(Constants.MSG_DAMAGED_XML_FILE,
							Constants.DEFAULT_ARCHIVE_FILE_NAME));
		}

		int numberOfArchivedTasks = Constants.ZEROTH;

		for (int i = 0; i < taskVector.size();) {
			if (taskVector.get(i).isCompleted()) {
				archivedTaskVector.add(taskVector.get(i));
				taskVector.remove(i);
				numberOfArchivedTasks++;
			} else {
				i++;
			}
		}

		archiveStorage.saveToFile(archivedTaskVector);
		sortTasks(Constants.DEFAULT_SORT);
		
		return numberOfArchivedTasks;
	}


	// End of Archive

	// @author A0096638M
	// Display Archive

	private static String displayArchive() {
		try {
			if (archiveStorage.readFromFile().isEmpty()) {
				return Constants.MSG_EMPTY_ARCHIVE_VECTOR;
			} else {
				addToArchivedVector(archiveStorage.readFromFile());
				return Constants.MSG_ARCHIVE_DISPLAYED;
			}
		} catch (DamagedXmlFileException e) {
			return String.format(Constants.MSG_DAMAGED_XML_FILE,
					Constants.DEFAULT_ARCHIVE_FILE_NAME);
		}
	}

	private static void addToArchivedVector(Vector<Task> archiveVector) {
		archivedTaskVector.clear();

		for (int i = 0; i < archiveVector.size(); i++) {
			String description = new String(archiveVector.get(i).getDescription());

			Calendar start = Calendar.getInstance();
			Calendar end = Calendar.getInstance();
			boolean isDone = archiveVector.get(i).isCompleted();

			if (archiveVector.get(i).getStartDate() != null) {
				start.set(Calendar.YEAR,
						archiveVector.get(i).getStartDate().get(Calendar.YEAR));
				start.set(Calendar.MONTH,
						archiveVector.get(i).getStartDate().get(Calendar.MONTH));
				start.set(Calendar.DATE,
						archiveVector.get(i).getStartDate().get(Calendar.DATE));
				start.set(Calendar.HOUR_OF_DAY, archiveVector.get(i)
						.getStartDate().get(Calendar.HOUR_OF_DAY));
				start.set(Calendar.MINUTE, archiveVector.get(i).getStartDate()
						.get(Calendar.MINUTE));
				start.set(Calendar.SECOND, archiveVector.get(i).getStartDate()
						.get(Calendar.SECOND));
				start.set(Calendar.MILLISECOND, archiveVector.get(i)
						.getStartDate().get(Calendar.MILLISECOND));
			}

			if (archiveVector.get(i).getEndDate() != null) {
				end.set(Calendar.YEAR,
						archiveVector.get(i).getEndDate().get(Calendar.YEAR));
				end.set(Calendar.MONTH,
						archiveVector.get(i).getEndDate().get(Calendar.MONTH));
				end.set(Calendar.DATE,
						archiveVector.get(i).getEndDate().get(Calendar.DATE));
				end.set(Calendar.HOUR_OF_DAY, archiveVector.get(i).getEndDate()
						.get(Calendar.HOUR_OF_DAY));
				end.set(Calendar.MINUTE,
						archiveVector.get(i).getEndDate().get(Calendar.MINUTE));
				end.set(Calendar.SECOND,
						archiveVector.get(i).getEndDate().get(Calendar.SECOND));
				end.set(Calendar.MILLISECOND, archiveVector.get(i).getEndDate()
						.get(Calendar.MILLISECOND));
			}

			if (archiveVector.get(i).getStartDate() == null) {
				start = null;
			}

			if (archiveVector.get(i).getEndDate() == null) {
				end = null;
			}

			Task archiveTask = new Task(description, start, end, isDone);
			archivedTaskVector.add(archiveTask);
		}
	}


	// End of Display Archive

	// @author A0096638M
	// Today's Tasks

	public static String getTodayTasks() {
		updateTodayTasksVector();
		return Constants.EMPTY_STRING;
	}

	// End of Today's Tasks

	// @author A0096638M
	// Next Week Tasks

	public static String getNextWeekTasks() {
		updateNextWeekTasksVector();
		
		return Constants.EMPTY_STRING;
	}

	// End of Next Week Tasks

	// @author A0118386X
	//Load

	private static String loadTaskVectorFromFile(String[] args) {
		String oldFileName = activeStorage.getFileName();
		String fileName = args[Constants.INDEX_INPUT];

		if (!fileName.endsWith(Constants.XML_EXTENSION)) {
			fileName += Constants.XML_EXTENSION;
		}

		File f = new File(fileName);

		if (f.exists()) {
			try {
				activeStorage = new Storage(fileName);
				taskVector = activeStorage.readFromFile();
				return String.format(Constants.MSG_LOADED_FROM_STORAGE,
						activeStorage.getFileName());
			} catch (InvalidFileNameException e) {
				return Constants.MSG_INVALID_FILE_NAME;
			} catch (DamagedXmlFileException e) {
				try {
					activeStorage = new Storage(oldFileName);
					try {
						taskVector = activeStorage.readFromFile();
					} catch (DamagedXmlFileException e1) {
						DisplayAndCommandTextComposite.showToUser(String
								.format(Constants.MSG_DAMAGED_XML_FILE,
										Constants.DEFAULT_FILE_NAME));
					}
				} catch (InvalidFileNameException e1) {
					DisplayAndCommandTextComposite
					.showToUser(Constants.MSG_INVALID_FILE_NAME);
				}

				return String.format(Constants.MSG_DAMAGED_XML_FILE, fileName);
			}
		} else {
			return String.format(Constants.MSG_FILE_DOES_NOT_EXIST, fileName);
		}
	}

	//End of load

	// @author A0118386X
	//Save

	private static String saveTaskVectorToFile(String fileName) {
		try {
			activeStorage = new Storage(fileName);
			activeStorage.saveToFile(taskVector);

			return String.format(Constants.MSG_SAVED_TO_STORAGE,
					activeStorage.getFileName());
		} catch (InvalidFileNameException e) {
			return String.format(Constants.MSG_INVALID_FILE_NAME, fileName);
		}
	}

	// End of save

	// @author A0096638M
	//Float

	private static String getFloat() {
		searchVector.clear();
		
		for (int i = 0; i < taskVector.size(); i++) {
			if (isFloat(taskVector.get(i))) {
				searchVector.add(taskVector.get(i));
			}
		}
		
		return Constants.COMMAND_SEARCH;
	}

	//End of Float

	// @author A0096638M
	// Other functions
	// Booleans

	private static boolean isFloat(Task task) {
		return !task.hasStartDate() && !task.hasEndDate();
	}

	private static boolean isBefore(Calendar start, Calendar end) {
		return start.before(end);
	}

	private static boolean isNegative(int number) {
		return number < Constants.ZEROTH;
	}

	private static boolean isModify(String[] args) {
		if (args.length == Constants.LENGTH_NO_ARGUMENTS) {
			return false;
		} else {
			return isCorrectModifyArgument(args)
					&& isCorrectModifyTaskNumber(args);
		}
	}

	private static boolean isCorrectModifyTaskNumber(String[] args) {
		return Parser.isInteger(args[Constants.INDEX_MODIFY_TASK_NUMBER])
				&& Integer.parseInt(args[Constants.INDEX_MODIFY_TASK_NUMBER]) <= taskVector
				.size();
	}

	private static boolean isCorrectModifyArgument(String[] args) {
		return args[Constants.INDEX_MODIFY_ARGUMENT]
				.equalsIgnoreCase(Constants.MODIFY_DESCRIPTION)
				|| args[Constants.INDEX_MODIFY_ARGUMENT]
						.equalsIgnoreCase(Constants.MODIFY_START_DATE)
						|| args[Constants.INDEX_MODIFY_ARGUMENT]
								.equalsIgnoreCase(Constants.MODIFY_END_DATE);
	}

	private static boolean hasWord(String searchword, int index,
			Vector<Task> tempSearchVector) {
		return tempSearchVector.get(index).getDescription().toLowerCase()
				.contains(searchword.toLowerCase());
	}

	private static boolean hasNoArguments(String argument) {
		return argument.equals(Constants.EMPTY_STRING);
	}	

	private static boolean isAcceptableArgumentsLength(String input) {
		String[] args = input.split(Constants.BLANK_SPACE);

		for (int i = 0; i < args.length; i++) {
			if (!(args[i].length() <= Constants.LENGTH_ACCEPTABLE_TASK_DESCRIPTION)) {
				return false;
			}
		}

		return true;
	}

	private static boolean hasDate(String searchword, int index) {
		return taskVector.get(index).getStartDateStringNoSlash()
				.contains(searchword)
				|| taskVector.get(index).getEndDateStringNoSlash()
				.contains(searchword);
	}

	private static boolean hasInput(String input) {
		return (!input.equals(Constants.EMPTY_STRING));
	}

	// Getters

	private static String getSearchTodayDate() {
		Calendar cal = Calendar.getInstance();
		String day = String.format(Constants.FORMAT_DATE_COMPONENT,
				cal.get(Calendar.DAY_OF_MONTH));
		String month = String.format(Constants.FORMAT_DATE_COMPONENT,
				cal.get(Calendar.MONTH));
		String year = String.format(Constants.FORMAT_DATE_COMPONENT,
				cal.get(Calendar.YEAR));

		return day + month + year;
	}

	private static Calendar getNow() {
		return Calendar.getInstance();
	}

	// Updater

	private static void updateUndoState() {
		updateListOfRedoActions();

		int indexOfLastAction = listOfUndoActions.size()
				- Constants.ADJUST_INDEX_BY_1;

		updateCurrentTaskVector(listOfUndoActions.get(indexOfLastAction));
		listOfUndoActions.removeElementAt(indexOfLastAction);
	}

	private static void updateListOfRedoActions() {
		Vector<Task> previousActions = new Vector<Task>();

		for (int i = 0; i < taskVector.size(); i++) {
			previousActions.add(taskVector.get(i));
		}

		listOfRedoActions.add(previousActions);
	}

	private static void updateCurrentTaskVector(Vector<Task> previousActions) {
		taskVector.clear();

		for (int i = 0; i < previousActions.size(); i++) {
			taskVector.add(previousActions.get(i));
		}
	}

	private static void updateRedoState() {
		updateListOfUndoActions();

		int indexOfLastAction = listOfRedoActions.size()
				- Constants.ADJUST_INDEX_BY_1;

		updateCurrentTaskVector(listOfRedoActions.get(indexOfLastAction));
		listOfRedoActions.removeElementAt(indexOfLastAction);
	}

	private static void updateListOfUndoActions() {
		Vector<Task> previousActions = new Vector<Task>();

		for (int i = 0; i < taskVector.size(); i++) {
			String description = new String(taskVector.get(i).getDescription());

			Calendar start = Calendar.getInstance();
			Calendar end = Calendar.getInstance();
			boolean isDone = taskVector.get(i).isCompleted();

			createStartCalendar(i, start);
			createEndCalendar(i, end);

			if (taskVector.get(i).getStartDate() == null) {
				start = null;
			}

			if (taskVector.get(i).getEndDate() == null) {
				end = null;
			}

			Task oldTask = new Task(description, start, end, isDone);
			previousActions.add(oldTask);
		}

		listOfUndoActions.add(previousActions);
	}

	private static void createEndCalendar(int i, Calendar end) {
		if (taskVector.get(i).getEndDate() != null) {
			end.set(Calendar.YEAR,
					taskVector.get(i).getEndDate().get(Calendar.YEAR));
			end.set(Calendar.MONTH,
					taskVector.get(i).getEndDate().get(Calendar.MONTH));
			end.set(Calendar.DATE,
					taskVector.get(i).getEndDate().get(Calendar.DATE));
			end.set(Calendar.HOUR_OF_DAY, taskVector.get(i).getEndDate()
					.get(Calendar.HOUR_OF_DAY));
			end.set(Calendar.MINUTE,
					taskVector.get(i).getEndDate().get(Calendar.MINUTE));
			end.set(Calendar.SECOND,
					taskVector.get(i).getEndDate().get(Calendar.SECOND));
			end.set(Calendar.MILLISECOND, taskVector.get(i).getEndDate()
					.get(Calendar.MILLISECOND));
		}
	}

	private static void createStartCalendar(int i, Calendar start) {
		if (taskVector.get(i).getStartDate() != null) {
			start.set(Calendar.YEAR,
					taskVector.get(i).getStartDate().get(Calendar.YEAR));
			start.set(Calendar.MONTH,
					taskVector.get(i).getStartDate().get(Calendar.MONTH));
			start.set(Calendar.DATE,
					taskVector.get(i).getStartDate().get(Calendar.DATE));
			start.set(Calendar.HOUR_OF_DAY, taskVector.get(i)
					.getStartDate().get(Calendar.HOUR_OF_DAY));
			start.set(Calendar.MINUTE, taskVector.get(i).getStartDate()
					.get(Calendar.MINUTE));
			start.set(Calendar.SECOND, taskVector.get(i).getStartDate()
					.get(Calendar.SECOND));
			start.set(Calendar.MILLISECOND, taskVector.get(i)
					.getStartDate().get(Calendar.MILLISECOND));
		}
	}

	private static void clearListOfRedoActions() {
		listOfRedoActions.removeAllElements();
	}

	public static void updateTodayTasksVector() {
		todayTasks.clear();

		String today = getSearchTodayDate();
		for (int i = 0; i < taskVector.size(); i++) {
			if (taskVector.get(i).getEndDateStringNoSlash()
					.equalsIgnoreCase(today)
					&& !taskVector.get(i).isCompleted()) {
				todayTasks.add(taskVector.get(i));
			}
		}
	}

	public static void updateNextWeekTasksVector() {
		nextWeekTasks.clear();

		Calendar start = getNow();
		Calendar end = getNow();
		end.add(Calendar.DATE, Constants.DAYS_IN_ONE_WEEK);

		while (isBefore(start, end)) {
			String current = Parser.parseDateString(start);
			for (int i = 0; i < taskVector.size(); i++) {
				if (taskVector.get(i).getEndDateStringNoSlash()
						.equalsIgnoreCase(current)
						&& !taskVector.get(i).isCompleted()) {
					nextWeekTasks.add(taskVector.get(i));
				}
			}

			start.add(Calendar.DATE, Constants.ONE_DAY);
		}
	}

	// Determine	

	private static int determineMaxNumberOfRedo(String[] args) {
		int numberOfRedoRequested = Integer
				.parseInt(args[Constants.INDEX_INPUT]);

		if (numberOfRedoRequested <= listOfRedoActions.size()) {
			return numberOfRedoRequested;
		}

		return listOfRedoActions.size();
	}

	private static int determineMaxNumberOfUndo(String[] args) {
		int numberOfUndoRequested = Integer
				.parseInt(args[Constants.INDEX_INPUT]);

		if (numberOfUndoRequested <= listOfUndoActions.size()) {
			return numberOfUndoRequested;
		}

		return listOfUndoActions.size();
	}

	// Display info

	public static void notifyNumberOfTasksToday(Vector<Task> taskVector) {
		switch (taskVector.size()) {
			case Constants.ZEROTH:
				DisplayAndCommandTextComposite
				.showToUser(Constants.FORMAT_TASKS_DUE_TODAY_NONE);
				break;
	
			case Constants.FIRST:
				DisplayAndCommandTextComposite
				.showToUser(Constants.FORMAT_TASKS_DUE_TODAY_ONE);
				break;
	
			default:
				DisplayAndCommandTextComposite.showToUser(String.format(
						Constants.FORMAT_TASKS_DUE_TODAY_MULTIPLE,
						taskVector.size()));
				break;
		}
	}

	private static void notifyNumberOfFoundTasks(Vector<Task> taskVector) {
		int numberOfTasks = taskVector.size();

		switch (numberOfTasks) {
			case Constants.ZEROTH:
				DisplayAndCommandTextComposite
				.showToUser(Constants.MSG_NOTIFY_NO_TASK_FOUND);
				break;
	
			case Constants.FIRST:
				DisplayAndCommandTextComposite
				.showToUser(Constants.MSG_NOTIFY_ONE_TASK_FOUND);
				break;
	
			default:
				DisplayAndCommandTextComposite.showToUser(String.format(
						Constants.MSG_NOTIFY_MANY_TASK_FOUND, numberOfTasks));
		}
	}

}
